package tool.classloader;

import java.io.File;
import java.io.FileFilter;
import java.util.HashMap;

/**rappresenta l'oggetto che, dato un sottoinsieme del file System,
 * carica solo le classi che estendono una particolare classe padre.
 * In caso in cui una classe sia presente nel sottofile system ma non
 * estenda la particolare classe padre, il LocalObjectClassLoader
 * non deve consentire il caricamento della particolare classe. 
 * 
 * @author Koldar
 * @version 1.1
 */
public class LocalObjectClassLoader extends FileSystemClassLoader{
	/*TODO ho notato che per creare la hasmap le classi vengono comunque caricare tramite LoadURL... a questo punto non sarebbe
	 * piu' comodo fare una hashmpa<String,Class<?>> in modo da avere gli oggetti  Class<?> subito? altrimenti
	 * a livello applicativo dopo devo comunque ricaicarle :P
	 */
	/**indica la classe padre di qualunque classe caricabile da questo ClassLoader*/
	protected Class<?> classType;
	
	/**costruisce un nuovo classLoader <strong>senza</strong> impostare
	 * i 2 parametri base della classe:
	 * <ul>
	 *  <li>{@link #classType}</li>
	 *  <li>directory</li>
	 * </ul>
	 * 
	 */
	public LocalObjectClassLoader(){
		super();
	}
	
	public LocalObjectClassLoader(Class<?> classtype){
		this((File)null,classtype,false);
	}
	
	public LocalObjectClassLoader(File directory,Class<?> classtype,boolean scan){
		super(directory);
		this.classType=classtype;
		if (scan){
			this.scanDirectory();
		}
	}
	public LocalObjectClassLoader(String directory,Class<?> classtype,boolean scan){
		this(new File(directory),classtype,scan);
	}
	
	@Override
	protected HashMap<String,String> scanDirectory(File rootdir,String relativepath){
		File[] directorylist=rootdir.listFiles((FileFilter)DIRECTORYFILTER);
		HashMap<String,String> result=new HashMap<String,String>();
		if (directorylist.length!=0){//ci sono directory nell'albero
			for (int i=0;i<directorylist.length;i++){
				result.putAll(this.scanDirectory(
						directorylist[i],
						relativepath+(rootdir.getName().equals(directory.getName())?"":".")+directorylist[i].getName()));
			}
		}
		//PIEDE DI RICORSIONE: non ci sono directory nell'albero
		String[] filelist=rootdir.list(CLASSFILTER);
		for (int i=0;i<filelist.length;i++){
			try{
				//TODO secondo qua potresti utilizzare instanceof invece di questo metodo ricorsivo!
				if (this.isChildofSuperClass(this.loadFromURL(relativepath+"."+FileSystemClassLoader.getFileName(filelist[i])))){
					result.put(FileSystemClassLoader.getFileName(filelist[i]),relativepath+"."+FileSystemClassLoader.getFileName(filelist[i]));
				}
			} catch (NoClassDefFoundError e){
				//in caso la classe non pu� essere caricata perché richiede altri oggetti esterni al classfile
				//il LocalObjectCalssLoader per sicurezza non ammette tale classe tra quelle caricabili
				//DEBUG System.out.println(filelist[i]+" non verrà inclusa! -.-'");
			} catch (ClassNotFoundException e) {
				//DEBUG System.out.println(filelist[i]+" non verrà inclusa! -.-'");
				//FIXME la classe non e' stata trovata. questo mi è accaduto con Main.class in Elaborato2011-2012 che si trovava
				//nel default package... l'algoritmo di scandirectory quindi potrebbe essere lacunoso!
			}
		}
		return result;
	}
	
	
	
	/**
	 * @return the classType
	 */
	public Class<?> getClassType() {
		return classType;
	}
	/**setta la nuova radice di tutte le classi da caricare e azzera la <tt>classfiles</tt>
	 * 
	 * @param classType the classType to set
	 */
	public void setClassType(Class<?> classType) {
		this.classType = classType;
		this.classfiles.clear();
	}
	/**
	 * 
	 * @param son la classe che bisogna stabilire se e' figlia o meno di {@link #classType}
	 * @return TRUE se son e' figlia (anche indiretta) di {@link #classType}, FALSE altrimenti
	 */
	protected final boolean isChildofSuperClass(Class<?> son){
		if (son.getSuperclass()==null){
			return false;
		}
		return ((son.getSuperclass().equals(this.classType))?true:isChildofSuperClass(son.getSuperclass()));
	}
	protected static final boolean isChildofSuperClass(Class<?> son,Class<?> father){
		if (son.getSuperclass()==null){
			return false;
		}
		return (son.getSuperclass().equals(father)?true:isChildofSuperClass(son.getSuperclass(),father));
	}
}
